package gweb

import (
	"context"
	"embed"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"time"

	"gitee.com/makitdone/gweb/v2/conf"
	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	"github.com/mitchellh/mapstructure"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

type EventHandler func()

type GinApp struct {
	*gin.Engine

	Directory string
	DB        *DB
	Models    []any
	JWT       *JWT
	Tasks     *TaskManager
	Viper     *viper.Viper
	// AuthCtrl  types.IAuthController

	config      *conf.AppConfig
	resource    *embed.FS
	configPath  string
	state       AppState
	logger      *conf.Logger
	rootCommand *cobra.Command
	handlers    []AppHandler
	variables   []Variable
	events      map[string][]EventHandler
}

func Application(opts ...gin.OptionFunc) *GinApp {
	// 获取系统目录
	dir, err := os.Getwd()
	if err != nil {
		log.Fatal("未知的运行路径！")
	}
	app := &GinApp{
		Directory:   dir,
		Models:      make([]any, 0),
		config:      &conf.AppConfig{},
		Tasks:       NewTaskManager(),
		rootCommand: &cobra.Command{},
		handlers:    make([]AppHandler, 0),
		variables:   make([]Variable, 0),
		events:      make(map[string][]EventHandler),
	}
	app.Engine = gin.Default(opts...)

	app.Use(func(ctx *gin.Context) {
		c := NewContext(app, ctx)
		middlewareRecover()(c)
	})

	app.Use(func(c *gin.Context) {
		middlewareCors(app)(c)
	})

	return app
}

func (app *GinApp) SetResource(resource embed.FS) {
	app.resource = &resource
}

// 获取配置
func (app *GinApp) GetConfig() *conf.AppConfig {
	return app.config
}

// 绑定自定义配置
func (app *GinApp) BindConfig(key string, dest any) error {
	value := app.Viper.Get(key)
	if value == nil {
		return fmt.Errorf("config key '%s' not found", key)
	}
	err := mapstructure.Decode(value, dest)
	if err != nil {
		return fmt.Errorf("failed to decode config value for key '%s': %w", key, err)
	}
	return nil
}

func (app *GinApp) InitEnv(env string) error {
	if env == string(conf.EnvProd) {
		gin.SetMode(gin.ReleaseMode)
	}

	if app.resource != nil {
		configFile := fmt.Sprintf("resource/config/config-%s.yaml", env)
		if f, err := app.resource.Open(configFile); err != nil {
			log.Fatalf("读取配置文件 %s 失败：%v", configFile, err)
		} else {
			app.Viper = app.makeViper(f)
		}
	} else if app.configPath != "" {
		configName := fmt.Sprintf("config-%s.yaml", env)
		filePath := filepath.Join(app.configPath, configName)
		file, err := os.Open(filePath)
		if err != nil {
			return fmt.Errorf("failed to open config file: %w", err)
		}
		defer file.Close()

		app.Viper = app.makeViper(file)
	} else {
		panic("缺少配置")
	}

	app.logger = app.config.Zap.Init()
	app.JWT = &JWT{JwtConfig: app.config.JWT}
	app.DB = Connect(app.config.DB)

	return nil
}

func (app *GinApp) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
	ginhandlers := []gin.HandlerFunc{}
	for _, handler := range handlers {
		ginhandlers = append(ginhandlers, func(ctx *gin.Context) {
			c := &Context{Context: ctx, App: app}
			handler(c)
		})
	}
	return &RouterGroup{
		RouterGroup: app.Engine.Group(relativePath, ginhandlers...),
		App:         app,
	}
}

func (app *GinApp) Run(addr string) {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				// app.Logger.Error("Task manager error: ", err)
				fmt.Println("Task manager error: ", err)
			}
		}()

		app.Tasks.Start() // 异步运行
	}()

	if addr == "" {
		host := app.config.Host
		port := app.config.Port
		addr = fmt.Sprintf("%s:%d", host, port)
	}
	err := app.Engine.Run(addr)
	if err != nil {
		return
	}

	serv := &http.Server{
		Addr:    addr,
		Handler: app.Engine,
	}

	go func() {
		if err := serv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt)
	<-quit
	log.Println("Shutdown Server ...")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := serv.Shutdown(ctx); err != nil {
		log.Fatal("Server shutdown: ", err)
	}
	log.Println("Server exiting")
}

// ------------------- 命令 --------------------

// 从命令行执行
func (app *GinApp) RunFromCommand() {
	app.buildCommandLine()
	app.rootCommand.Execute()
}

// 构建命令行
func (app *GinApp) buildCommandLine() {
	app.rootCommand.AddCommand(
		serve(app),
		migrate(app),
	)
}

// 注册命令
func (app *GinApp) RegisterCommand(command *cobra.Command) {
	app.rootCommand.AddCommand(command)
}

func (app *GinApp) StaticDirectory() string {
	dirs := app.config.Directories
	static := "static" // 默认静态资源目录
	if dirs != nil {
		static = app.config.Directories.Static
	}
	return filepath.Join(app.Directory, static)
}

func (app *GinApp) LogDirectory() string {
	return filepath.Join(app.Directory, app.config.Zap.Directory)
}

func (app *GinApp) makeViper(reader io.Reader) *viper.Viper {
	v := viper.New()
	v.SetConfigType("yaml")
	err := v.ReadConfig(reader)
	if err != nil {
		panic(fmt.Errorf("fatal error config file: %s", err))
	}
	v.WatchConfig()

	v.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("config file changed:", e.Name)
	})
	if err = v.Unmarshal(app.config); err != nil {
		fmt.Println(err)
	}
	return v
}
